/*******************************************************************************
 * Copyright (c) 2003 Boris Pruessmann and others. All rights reserved. This
 * program and the accompanying materials are made available under the terms of
 * the Common Public License v0.5 which accompanies this distribution, and is
 * available at http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors: Boris Pruessmann - Initial implementation, most code taken from
 * CVS plugin
 ******************************************************************************/
package net.sourceforge.perforce.ui.internal;

import com.google.common.collect.Lists;

import org.eclipse.jface.text.AbstractDocument;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DefaultLineTracker;
import org.eclipse.jface.text.GapTextStore;

import java.util.List;

/**
 * @version $Revision: 488 $
 * @author <a href="mailto:bpruessmann@users.sourceforge.net">Boris
 *         Pruessmann</a>
 */
public class ConsoleDocument extends AbstractDocument {
  //----------------------------------------------------------------------------
  // Public Constants

  public static final int COMMAND = 0;
  public static final int MESSAGE = 1;
  public static final int ERROR = 2;
  public static final int DELIMITER = 3;

  //----------------------------------------------------------------------------
  // Private Varibles

  private int[] lineTypes = null;
  private int currentLine = 0;

  // -------------------------------------------------------------------------
  // Public Constructors

  /** Constructor for ConsoleDocument. */
  public ConsoleDocument() {
    setTextStore(new GapTextStore(512, 1024));
    setLineTracker(new DefaultLineTracker());
    completeInitialization();
  }

  //----------------------------------------------------------------------------
  // -- Public Methods

  /** Clears the console document. */
  public void clear() {
    lineTypes = null;
    currentLine = 0;
    set(""); //$NON-NLS-1$
  }

  /** Gets the line type for the line containing the specified offset. */
  public int getLineType(int offset) {
    try {
      int line = getLineOfOffset(offset);
      if (line < currentLine) return lineTypes[line];
    } catch (BadLocationException e) {
      // TODO: PerforceUIPlugin.log(PerforceException.wrapException(e));
    }

    return 0;
  }

  /** Appends a line of the specified type to the end of the console. */
  public void appendConsoleLine(int type, String line, boolean purgeExcess) {
    if (lineTypes == null) {
      lineTypes = new int[16];
    } else if (currentLine >= lineTypes.length) {
      int[] oldLineTypes = lineTypes;
      lineTypes = new int[oldLineTypes.length * 2];
      System.arraycopy(oldLineTypes, 0, lineTypes, 0, oldLineTypes.length);
    }

    lineTypes[currentLine++] = type;
    try {
      replace(getLength(), 0, line + "\n"); //$NON-NLS-1$
      if (purgeExcess && type == COMMAND) {
        keepPreviousCommands(2);
      }
    } catch (BadLocationException e) {
      // TODO: PerforceUIPlugin.log(PerforceException.wrapException(e));
    }
  }

  //----------------------------------------------------------------------------
  // - Private Methods

  /** Return the indices of the lines that contain command strings */
  private int[] getCommandLines() {
    List<Integer> commandLineList = Lists.newArrayList();
    for (int i = 0; i < currentLine; i++) {
      if (lineTypes[i] == COMMAND) {
        commandLineList.add(Integer.valueOf(i));
      }
    }
    int[] commandLines = new int[commandLineList.size()];
    int i = 0;
    for (Integer integer : commandLineList) {
      commandLines[i++] = integer.intValue();
    }
    return commandLines;
  }

  /** Purge all but the output of the last N commands from the document */
  private void keepPreviousCommands(int number) throws BadLocationException {
    // Get the index of the line and character to keep
    int[] commandLines = getCommandLines();
    if (commandLines.length <= number) return;
    int lineIndex = commandLines[commandLines.length - number];
    int characterIndex = getLineOffset(lineIndex);

    // Keep everything from the character to the end
    set(get(characterIndex, getLength() - characterIndex));

    // Adjust the line types
    int[] oldLineTypes = lineTypes;
    lineTypes = new int[oldLineTypes.length];
    System.arraycopy(oldLineTypes, lineIndex, lineTypes, 0, oldLineTypes.length - lineIndex);
    currentLine -= lineIndex;
  }
}
